package org.vacoor.xqq.ui.dynamictree;

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * User: Vacoor
 */
public class NotifyTree extends JTree {
    private Notifier notifier;

    public NotifyTree(TreeModel model) {
        super(model);
        getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        setToggleClickCount(1);
        setRootVisible(false);
    }

    /**
     * 根据 id 查找
     */
    public IdentifiableAndObservableDataNode getIdentifiableAndObservableNode(long id) {
        TreeModel model = getModel();
        if (model instanceof DataNodeMonitorTreeModel) {
            return ((DataNodeMonitorTreeModel) model).getIdentifiableAndObservableNode(id);
        }
        return getIdentifiableAndObservableNode((DefaultMutableTreeNode) model.getRoot(), id);
    }

    private IdentifiableAndObservableDataNode getIdentifiableAndObservableNode(DefaultMutableTreeNode parent, long id) {
        Enumeration<TreeNode> nodes = parent.breadthFirstEnumeration();
        while (nodes.hasMoreElements()) {
            TreeNode node = nodes.nextElement();
            if (!(node instanceof IdentifiableAndObservableDataNode)) {
                continue;
            }
            IdentifiableAndObservableDataNode identifiableNode = (IdentifiableAndObservableDataNode) node;
            if (identifiableNode.getId() == id) {
                return identifiableNode;
            }
        }
        return null;
    }

    public void addNotify(long id) {
        IdentifiableAndObservableDataNode n = getIdentifiableAndObservableNode(id);
        if (n instanceof ShakeableNode) {
            addNotify((ShakeableNode) n);
        }
    }

    public void addNotify(ShakeableNode node) {
        ensureNotifier();
        notifier.addNotify(node);
    }

    public void removeNotify(long id) {
        if (notifier == null) {
            return;
        }
        notifier.removeNotify(id);
    }

    public void removeNotify(ShakeableNode node) {
        if (notifier == null) {
            return;
        }
        notifier.removeNotify(node);
    }

    public void repaint(final TreeNode treeNode) {

        // TODO 要不要这样
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) treeNode;
                TreePath path = new TreePath(node.getPath());
                Rectangle bounds = getPathBounds(path);
                int row = getRowForPath(path);
                // 可见
                if (-1 != row && bounds != null) {
                    repaint(bounds);
                }
            }
        });
    }

    public void repaint(int row) {
        // 如果 row 不存在返回null
        repaint(this.getPathForRow(row));
    }

    public void repaint(TreePath path) {
        // 如果 path == null || path 不存在, 则返回null
        Rectangle bounds = this.getPathBounds(path);
        if (bounds == null) {
            return;
        }
        this.repaint(bounds);
    }

    private void ensureNotifier() {
        if (notifier != null) {
            return;
        }
        synchronized (NotifyTree.class) {
            if (notifier == null) {
                notifier = new Notifier();
            }
        }
    }

    // 唤醒管理器
    protected class Notifier implements ActionListener {
        private Timer timer = new Timer(200, this);
        private Map<Long, ShakeableNode> notifyList = new ConcurrentHashMap<Long, ShakeableNode>();

        public void addNotify(ShakeableNode node) {
            if (node == null) {
                return;
            }
            notifyList.put(node.getId(), node);
            node.setShaking(true);
            if (notifyList.size() > 0) {
                timer.start();
            }
        }

        public void removeNotify(long id) {
            ShakeableNode node = notifyList.remove(id);
            if (node == null) {
                return;
            }
            node.setShaking(false);
            repaint(node);
            TreeNode p = node.getParent();
            if (p instanceof ShakeableNode) {
                ((ShakeableNode) p).setShaking(false);
                repaint(p);
            }
            if (notifyList.size() < 1) {
                timer.stop();
            }
        }

        public void removeNotify(ShakeableNode shakeableNode) {
            removeNotify(shakeableNode.getId());
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            // 这里不通过 row 遍历所有可见节点, 容易出现并发问题, 且一般队列中元素会比显示的元素少
            for (ShakeableNode n : notifyList.values()) {
                repaint(n);
                TreeNode parent = n.getParent();
                if (parent != null && parent instanceof ShakeableNode) {
                    ShakeableNode p = (ShakeableNode) parent;
                    // 如果没有展开
                    if (!isExpanded(new TreePath(p.getPath()))) {
                        p.setShaking(true);
                        repaint(p);
                    } else if (p.isShaking()) { // 展开 但仍是 shaking, 则取消
                        p.setShaking(false);
                        repaint(p);
                    }
                }
            }
        }
    }
}
